// Copyright (C) 2011 by Christopher Diggins
// Usage is permitted under the terms of the MIT License 1.0. 	

/** 
	\file 
	The main header file for CVM.
*/

#ifndef CVML_H
#define CVML_H

#include <assert.h>
#include <malloc.h>
//#include <math.h>
#include <string.h>
#include <stdio.h>

#define HEAP_BUFFER_COUNT 1024	//< Size of buffers for dynamic allocator.
#define STACK_SIZE 32			//< Size of primary and auxiliary stack combined.
#define ARRAY_MIN_SIZE 4		//< Minimum number of items in a non-empty array. 
#define TABLE_GROWTH_FACTOR 2	//< The factor that a hash table grows by when resized. 
#define TABLE_MAX_LOAD 128		//< Maximum load factor of a hash before it is resized. Expressed as the numerator of fraction over 256. 
#define TABLE_MIN_SIZE 8		//< The minimum of a table. 
#define MAX_REFS 0x80			//< The maximum number of references that will e counted

#define TYPE_ERROR()			assert(!"Type error")
#define BOUNDS_ERROR()			assert(!"Bounds error")
#define	UNCAUGHT_EXCEPTION()	assert(!"Exception not caught")

/** Opcode values. */
enum OpCode 
{ 
	#define OPCODE(OP, WIDTH, CAT, DESC) _##OP,
	#include "opcodes.h"
};

/** Flags used by structs and objects for internal purposes
	such as garbage collection. */
enum ObjectFlags
{
	DeletedFlag			= 1,
	DeletingFlag		= 1 << 1,
	VisitedFlag			= 1 << 2, 
	ConstantFlag		= 1 << 3,
	MaybeGarbageFlag	= 1 << 4,
};

/** Flags used by the VM. */
enum VMFlags
{
	GCPausedFlag		= 1,
	UncaughtException	= 1 << 1,	
};

/** Values to identify the different types. */
enum Types
{
	VByteType, 
	VIntType, 
	VFloatType, 
	VDoubleType,
	VectorType		= 16,
	ObjectType,
	FunctionType,
	TableType, 
	StoreType, 
	NativeFunType,
	ByteType, 
	IntType, 
	ShortType, 
	FloatType, 
	DoubleType,
	PairType,
	MarkerType,
	TypeIdType,
	PrimitiveType	= 32,
};

typedef unsigned char	byte;
typedef unsigned short	ushort;
typedef unsigned long	ulong;
typedef unsigned int	uint;

/** A dynamic array of bytes. */
typedef struct Array_Struct
{
	int cap;		//!< Capacity of array. 
	int cnt;		//!< Number of items in array. 
	int sz;			//!< Size of individual elements. 
	byte* data;		//!< Array data. 
}
Array;

/** A union of the different kinds of ddata that can be contained in a Val. */
typedef union ValData_Union
{
	// The following three must line up correctly.
	struct { void* first; void* second; } PairData; 
	struct { void* vtbl; Array* adata; } ObjectData;	
	struct { byte* code; Array* args; } FunctionData;	
	struct { byte val; } ByteData;
	struct { int val; } IntData;
	struct { long val; } LongData;
	struct { float val; } FloatData;
	struct { double val; } DoubleData; 
	struct { void* val; } PointerData; 
}
ValData;

/** The basic Value structure used by the VM. */
typedef struct Val_Struct
{
	byte		type;		//!< Type table information. 
	byte		flags;		//!< Various flags. 
	byte		refs;		//!< The number of references. 
	byte		padding;	//!< Not currently used. 
	ValData		data;		//!< Other data. 
} 
Val;	

/** A structure used by the object allocator to store 
	memory slots. */
typedef struct Buffer_Struct
{
	void* prev;		//!< Previous buffer.
	void* next;		//!< Next buffer.
	Val* cur;		//!< Next available slot for allocation.
	Val* end;		//!< End of dynamically allocated data.
	Val* data;		//!< Beginning of dynamically allocated data .
}
Buffer;

/** The memory allocator: stores a pointer to most 
	recently allocated buffer, and a pointer to the most 
	recently deleted Val. Note that objects when 
	deleted use the "data" pointer to point to the previously
	deleted object. This creates a linked list of 
	deleted objects in the heap. */
typedef struct Mem_struct
{
	Buffer* buffer;		//!< Most recently created buffer. 
	Val*	deleted;	//!< Most recently freed object. 
	int		garbage;	//!< Count of potential garbage. 
}
Mem;

/** Used for iterating over values in memory. 
	For example when copying memory or collecting
	memory. */
typedef struct MemIter_struct
{
	Buffer* buffer;		//!< Current buffer.
	Val*	val;		//!< Current value.
	Mem*	mem;		//!< Memory management struct.
}
MemIter;

/** Used for representing key value pairs in associative arrays. */
typedef struct KeyVal_Struct
{
	uint key;		//!< Hash code.
	Val* val;		//!< Associated value.
}
KeyVal;

/** All data used by a VM. A VM consists of a heap,
	two stacks (a main stack and an auxiliary stack), 
	a constant pool, an instruction stream, and 
	a current instruction pointer. */
typedef struct VM_Struct
{	
	int		flags;		//!< Flags, such as error flags. 
	Mem  	mem;		//!< Memory manager (heap). 
	Val**   stkbot;		//!< Stack bottom. 
	Val**   stktop;		//!< Stack top (begin of auxiliaray stack). 
	Val**	sp;			//!< Stack pointer. 
	Val**	ap;			//!< Auxiliary pointer. 
	Array	constants;	//!< Constant table. 
	Array	functions;	//!< Function table. 
	Array	codedata;	//!< Code table. 
	byte*	ip;			//!< Instruction pointer. 
}
VM;

/** A function pointer used when traversing the memory graph. */
typedef void(*VisitFxn)(Val*, void*);

/** A function pointer used when calling C functions. */
typedef void(*NativeFxn)(VM*);

#define		array_count(A)			((A)->cnt)
#define		array_element_size(A)	((A)->sz)
#define		array_num_bytes(A)		(array_count(A) * array_element_size(A))
#define		array_cap(A)			((A)->cap)
#define		array_pop(A)			((A)->cnt--)
#define		array_empty(A)			((A)->cnt == 0)
#define		array_at(T, A, N)		(((T*)(A)->data)[N])
#define		array_top(T, A)			(array_at(T, A, ((A)->cnt - 1)))
#define		array_begin(T, A)		(&array_at(T, A, 0))
#define		array_end(T, A)			(&array_at(T, A, (A)->cnt))

Array*		array_new(int sz);
Array*		array_init(Array* a, int sz);
void		array_clear(Array* a);
void		array_free(Array* a);
void		array_alloc(Array* a, int cnt);
void		array_push(Array* a, void* src, uint size);
void		array_grow(Array* a);
Array*		array_clone(Array* a);

#define		val_new_untyped()		(mem_val_alloc(&vm->mem))
#define		val_new(T)				(val_init(val_new_untyped(), T))
#define		val_from_int(X)			(val_from_int_impl(vm, (X)))
#define		val_from_float(X)		(val_from_float_impl(vm, (X)))
#define		val_from_double(X)		(val_from_double_impl(vm, (X)))
#define		val_is_unique(X)		((X)->refs == 1)
#define		val_is_const(X)			(val_has_flag((X), ConstantFlag)
#define		val_is_deleted(X)		(val_has_flag((X), DeletedFlag)
#define		val_is_table(X)			((X)->type == TableType)
#define		val_is_obj(X)			((X)->type == ObjectType)
#define		val_is_pair(X)			((X)->type == PairType)
#define		val_is_vec(X)			((X)->type < VectorType) 
#define		val_is_seq(X)			((X)->type < ObjectType) 
#define		val_has_array(X)		(val_is_vec(X) && val_to_array(X))
#define		val_to_array(X)			((X)->data.ObjectData.adata)
#define		val_to_obj(X)			((X)->data.ObjectData)
#define		val_to_byte(X)			((X)->data.ByteData.val)
#define		val_to_int(X)			((X)->data.IntData.val)
#define		val_to_float(X)			((X)->data.FloatData.val)
#define		val_to_double(X)		((X)->data.DoubleData.val)
#define		val_max_refs(X)			((X)->refs & MAX_REFS)
#define		val_set_flag(X, F)		((X)->flags |= (F))
#define		val_clear_flag(X, F)	((X)->flags &= ~(F))
#define		val_has_flag(X, F)		((X)->flags & (F)) 
#define		val_dup(X)				(val_dup_impl(vm, (X)))
#define		val_pop(X)				(val_del_ref(X))
#define		val_add_ref(X)			(val_add_ref_impl(vm, (X)))
#define		val_del(X)				(val_del_impl(vm, (X)))
#define		val_del_ref(X)			(val_del_ref_impl(vm, (X)))
#define		val_to_str(X)			(val_to_str_impl(vm, (X)))

Val*		val_init(Val* x, int type);
Val*		val_from_int_impl(VM* vm, int x);
Val*		val_from_float_impl(VM* vm, float x);
Val*		val_from_double_impl(VM* vm, double x);
Val*		val_dup_impl(VM* vm, Val* x);
Val*		val_add_ref_impl(VM* vm, Val* x);
void		val_del_impl(VM* vm, Val* x);
void		val_del_ref_impl(VM* vm, Val* x);
int			val_to_str_buffer(Val* x, char* s, int size);
Val*		val_to_str_impl(VM* vm, Val* x);

#define		obj_new()				(val_new(ObjectType))
#define		obj_pull(O)				(obj_pull_impl(vm, O))
#define		obj_array(O)			((O)->data.ObjectData.adata)
#define		obj_vtbl(O)				((Val*)(O)->data.ObjectData.vtbl)
#define		obj_set_vtbl(O, X)		((O)->data.ObjectData.adata = (Val*)(X))
#define		obj_empty(O)			(array_empty(obj_array(O)))
#define		obj_count(O)			(array_empty(obj_array(O)) ? 0 : obj_array(O)->cnt)
#define		obj_at(O, N)			(array_at(Val*, obj_array(O), (N)))
#define		obj_top(O)				(array_top(Val*, obj_array(O)))
#define		obj_begin(O)			(array_begin(Val*, obj_array(O)))
#define		obj_end(O)				(array_end(Val*, obj_array(O)))
#define		obj_push(O, X)			(array_push(obj_array(O), &X, sizeof(Val*)))
#define		obj_pop(O)				(val_del_ref(vm, obj_pull(O)))

#define		fun_new(F)				(fun_new_impl(vm, F))
#define		fun_code(F)				((F)->data.FunctionData.code)
#define		fun_args(F)				((F)->data.FunctionData.args)
#define		fun_arg_begin(F)		(array_begin(Val*, fun_args(F)))
#define		fun_arg_end(F)			(array_end(Val*, fun_args(F)))

#define		pair_first(X)			((X)->data.PairData.first)
#define		pair_second(X)			((X)->data.PairData.second)

Val*		obj_pull_impl(VM* vm, Val* obj);
Val*		obj_new_fun_impl(VM* vm, byte* f);

#define		vec_count(V)			(obj_count(V))
#define		vec_new(T, N)			(vec_new_impl(vm, T, N))
#define		vec_at(T, V, N)			(array_at(T, obj_array(V), N))
#define		vec_copy(V)				(vec_copy_impl(vm, V))
#define		vec_num_bytes(V)		(array_num_bytes(obj_array(V)))
#define		vec_get_val(V, N)		(vec_get_val_impl(V, val_new_untyped(), N))
#define		vec_set_val(V, X, N)	(vec_set_val_impl(V, X, N))

Val*		vec_copy_impl(VM* vm, Val* v);
Val*		vec_new_impl(VM* vm, int type, int count);
Val*		vec_get_val_impl(Val* v, Val* x, int n);
void		vec_set_val_impl(Val* v, Val* x, int n);
void		vec_map_op(VM* vm, byte code);
void		vec_zip_op(VM* vm, byte code);

#define		seq_count(XS)			(obj_count(XS))
#define		seq_cat(V0, V1)			(seq_cat_impl(vm, V0, V1))

Val*		seq_get(VM* vm, int n, Val* seq);
void		seq_set(VM* vm, int n, Val* x, Val* seq);
Val*		seq_clone(VM* vm, Val* xs);
Val*		seq_cat_impl(VM* vm, Val* v0, Val* v1);

#define		table_new()				(table_init(val_new(TableType)))
#define		table_alloc(TBL, N)		(table_alloc_impl(val_init(TBL, TableType), N))
#define		table_free(TBL)			(val_del_impl(vm, TBL))
#define		table_init(TBL)			(table_alloc(TBL, TABLE_MIN_SIZE))
#define		table_count(TBL)		(obj_array(TBL)->cnt)
#define		table_size(TBL)			(obj_array(TBL)->cap)
#define		table_at(TBL, N)		(array_at(KeyVal, obj_array(TBL), N))
#define		table_begin(TBL)		(&table_at(TBL, 0))
#define		table_end(TBL)			(&table_at(TBL, table_size(TBL)))
#define		table_load(TBL)			((table_size(TBL) * 256) / (table_count(TBL)))
#define		table_add(TBL, K, V)	(table_add_impl(vm, TBL, K, V))
#define		table_slot_occupied(KV)	((KV)->val)
#define		table_contains(TBL, K)	(table_slot_occupied(table_find(TBL, K)))
#define		table_remove(TBL, K)	(table_remove_impl(vm, TBL, K))

uint		table_hash(uint n);
Val*		table_alloc_impl(Val* table, uint n);
KeyVal*		table_find(Val* table, uint key);
void		table_free_impl(VM* vm, Val* table);
void		table_add_impl(VM* vm, Val* table, uint key, Val* val);
void		table_remove_impl(VM* vm, Val* table, uint key);
void		table_grow_impl(VM* vm, Val* table);

#define		mem_foreach(_ITER, _MEM) *_ITER = mem_first(_MEM); while (mem_next(_ITER))
#define		mem_val_visit(X) (mem_val_visit_with((X), NULL, NULL))

void		mem_val_free(Mem* m, Val* o);
Val*		mem_val_alloc(Mem* m);
void		mem_val_visit_with(Val* v, VisitFxn f, void* a);
void		mem_init(Mem* m);
void		mem_free(Mem* m);
MemIter		mem_first(Mem* m);
uint		mem_next(MemIter* iter);

#define		vm_gc_pause()		(vm->flags |= VMGCPauseFlag)
#define		vm_gc_resume()		(vm->flags &= ~VMGCPauseFlag)
#define		vm_throw(X)			(vm_throw_impl(vm, X))
#define		vm_pull()			(*vm->sp--)
#define		vm_pull_aux()		(*vm->ap++)
#define		vm_push(X)			(vm_push_impl(vm, X))
#define		vm_push_aux(X)		(vm_push_aux_impl(vm, X))
#define		vm_push_int(X)		(vm_push(val_from_int(X)))
#define		vm_push_float(X)	(vm_push(val_from_float(X)))
#define		vm_push_double(X)	(vm_push(val_from_double(X)))
#define		vm_push_fun(X)		(vm_push(obj_new_fun(X)))
#define		vm_pop()			(val_pop(vm_pull()))
#define		vm_pop_aux()		(val_pop(vm_pull_aux()))
#define		vm_top()			(*vm->sp)
#define		vm_top_aux()		(*vm->ap)
#define		vm_top_int()		(val_to_int(vm_top()))
#define		vm_at(N)			(vm->sp[(-N)])
#define		vm_aux_at(N)		(vm->ap[(-N)])
#define		vm_second()			(vm->sp[-1])
#define		vm_third()			(vm->sp[-2])
#define		vm_pull_int()		(vm_pull_int_impl(vm))
#define		vm_top_as_int()		(val_to_int(vm_top()))
#define		vm_top_as_flt()		(val_to_float(vm_top()))
#define		vm_fetch_byte()		(*vm->ip++)
#define		vm_fetch_short()	(vm_fetch_short_impl(vm))
#define		vm_fetch_long()		(vm_fetch_long_impl(vm))
#define		vm_fetch_ushort()	(vm_fetch_ushort_impl(vm))
#define		vm_fetch_ulong()	(vm_fetch_ulong_impl(vm))
#define		vm_fetch_float()	(vm_fetch_float_impl(vm))
#define		vm_fetch_double()	(vm_fetch_double_impl(vm))
#define		vm_constant(N) 		(array_at(Val*, &vm->constants, N))	
#define		vm_code_begin()		(array_begin(byte, &vm->codedata))
#define		vm_code_end()		(array_end(byte, &vm->codedata))
#define		vm_function(N)		(fun_new_impl(vm, vm_fun_begin() + N))
#define		vm_fun_begin()		(array_begin(byte, &vm->functions))
#define		vm_fun_end()		(array_end(byte, &vm->functions))
#define		vm_count()			(vm->sp - vm->stkbot + 1)
#define		vm_aux_count()		(vm->stktop - vm->ap + 1)
#define		vm_empty()			(vm->sp < vm->stkbot)
#define		vm_aux_empty()		(vm->ap > vm->stktop)

uint		vm_op_width(byte op);
void		vm_push_impl(VM* vm, Val* x);
void		vm_push_aux_impl(VM* vm, Val* x);
void		vm_init(VM* vm);
void		vm_clear(VM* vm);
void		vm_free(VM* vm);
int			vm_pull_int_impl(VM* vm);
void		vm_gc(VM* vm);
void		vm_get_stack(VM* vm, Val* dest);
void		vm_get_aux_stack(VM* vm, Val* dest);
uint		vm_num_constants(VM* vm);
void		vm_load_code(VM* vm, byte* src, uint size);
void		vm_run(VM* vm);
int			vm_next(VM* vm);
void		vm_dup_impl(VM* vm, int n);
void		vm_swap_impl(VM* vm, int n, int m);
void		vm_call_impl(VM* vm, Val* o);
void		vm_tailcall_impl(VM* vm, Val* o);
void		vm_clear_stack(VM* vm);
void		vm_clear_aux_stack(VM* vm);
void		vm_clear_stacks(VM* vm);
short		vm_fetch_short_impl(VM* vm);
long		vm_fetch_long_impl(VM* vm);
ushort		vm_fetch_ushort_impl(VM* vm);
ulong		vm_fetch_ulong_impl(VM* vm);
float		vm_fetch_float_impl(VM* vm);
double		vm_fetch_double_impl(VM* vm);
void		vm_eval(VM* vm, byte code);
void		vm_throw_impl(VM* vm, Val* x);
void		vm_exit(int n);

void		serialize_vm(VM* vm, Val* dest, int include_stacks);
void		serialize_blob(Val* dest, void* x, uint size);
void		serialize_blob_with_size(Val* dest, void* x, uint size);
Val*		serialize_get_object_from_blob(Val* src, uint n);
uint		serialize_num_objects_in_blob(Val* src);
void		serialize_object_array_as_blob(Val* dest, Val* src);
void		serialize_convert_object_pointers(Val* dest, Val* table);
void		serialize_get_object_data(Val* src, Val* dest, Val* table);
void		serialize_get_object_data_children(Val* src, Val* dest, Val* table);
void		serialize_get_all_array_entries(Val* odata, Val* table, Val* adata);
void		serialize_get_array_entries(Val* o, Val* table, Val* adata);
void		serialize_get_array_entry(Val* val, Val* table, Val* adata);

Val**		deserialize_object_as_blob(Val** src, Val* dest);
Val**		deserialize_object_array_as_blob(Val** src, Val* dest);
void		deserialize_restore_pointers(Val* dest, Val* os);
void		deserialize_restore_arrays(Val* dest, Val* os, Val* adata);
void		deserialize_vm(VM* vm, Val** src);

#endif 
